package com.ewerp.mud.core.session.sockets;

import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;

import com.ewerp.mud.core.lifecycle.ILifecycleListener;
import com.ewerp.mud.core.session.ISessionFactory;
import com.ewerp.mud.core.session.ISessionManager;

/**
 * Generate socket sessions for the ejMud by listening for incoming connection
 * requests and creating a new {@link ISession} for them.
 * 
 * @author cboyden
 * 
 */
public class EwerpSocketSessionFactory implements ISessionFactory, ILifecycleListener, Runnable {
    private static EwerpSocketSessionFactory instance = new EwerpSocketSessionFactory();

    protected ServerSocket serverSocket = null;
    protected int port = 5555;
    protected ISessionManager sessionManager = null;
    protected Boolean shutdown = false;

    protected Thread serverThread = null;

    public static EwerpSocketSessionFactory getInstance() {
        if (instance == null) {
            instance = new EwerpSocketSessionFactory();
        }
        return instance;
    }

    protected EwerpSocketSessionFactory() {
    }

    @Override
    public void run() {
        try {
            if (getServerSocket() == null) {
                setServerSocket(new ServerSocket(port));
            }

            getServerSocket().setSoTimeout(2000);

            Socket client = null;

            while (!isShutdown()) {
                try {
                    client = getServerSocket().accept();

                    // TODO: Check for blacklist
                    sessionManager.addSession(new EwerpSocketSession(client));
                } catch (SocketTimeoutException e) {
                    // This exists to allow the polling of the isShutdown state
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void registerSessionManager(ISessionManager sessionManager) {
        this.sessionManager = sessionManager;
    }

    @Override
    public void init() {
        serverThread = new Thread(this);
    }

    @Override
    public void start() {
        serverThread.start();
    }

    @Override
    public void stop() {
        synchronized (shutdown) {
            shutdown = true;
        }
        serverThread.interrupt();
    }

    protected ServerSocket getServerSocket() {
        return serverSocket;
    }

    /**
     * Package level method for unit testing. DO NOT CHANGE SCOPE.
     * @param serverSocket
     */
    void setServerSocket(ServerSocket serverSocket) {
        this.serverSocket = serverSocket;
    }

    protected boolean isShutdown() {
        synchronized (shutdown) {
            return shutdown;
        }
    }

}
